package com.zyb.beidada.scoring;

import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.zyb.beidada.common.ErrorCode;
import com.zyb.beidada.exception.BusinessException;
import com.zyb.beidada.exception.ThrowUtils;
import com.zyb.beidada.manager.AIManager;
import com.zyb.beidada.model.dto.question.QuestionAnswerDTO;
import com.zyb.beidada.model.dto.question.QuestionContentDTO;
import com.zyb.beidada.model.entity.App;
import com.zyb.beidada.model.entity.Question;
import com.zyb.beidada.model.entity.ScoringResult;
import com.zyb.beidada.model.entity.UserAnswer;
import com.zyb.beidada.model.enums.ScoringStrategyEnum;
import com.zyb.beidada.model.vo.QuestionVO;
import com.zyb.beidada.service.AppService;
import com.zyb.beidada.service.QuestionService;
import com.zyb.beidada.service.ScoringResultService;
import org.jetbrains.annotations.NotNull;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author 十八岁讨厌编程
 * @date 2025/5/7 22:28
 * @PROJECT_NAME beidada-backend
 * @description AI评分策略
 */

@Component
@ScoringStrategyRequire(appType = 1, scoringStrategy = 1)
public class AiTestScoringStrategy implements ScoringStrategy{

    @Resource
    private ScoringResultService scoringResultService;

    @Resource
    private QuestionService questionService;

    @Resource
    private AIManager aiManager;

    @Resource
    private RedissonClient redissonClient;

    private static final String AI_ANSWER_LOCK = "AI_ANSWER_LOCK";



    //引入本地缓存
    private Cache<String, String> cache = Caffeine.newBuilder()
            .maximumSize(1024)
            .expireAfterAccess(5, TimeUnit.MINUTES)
            .build();

    private static final String AI_TEST_SCORING_SYSTEM_MESSAGE = "你是一位严谨的判题专家，我会给你如下信息：\n" +
            "```\n" +
            "应用名称，\n" +
            "【【【应用描述】】】，\n" +
            "题目和用户回答的列表：格式为 [{\"title\": \"题目\",\"answer\": \"用户回答\"}]\n" +
            "```\n" +
            "\n" +
            "请你根据上述信息，按照以下步骤来对用户进行评价：\n" +
            "1. 要求：需要给出一个明确的评价结果，包括评价名称（尽量简短）和评价描述（尽量详细，大于 200 字）\n" +
            "2. 严格按照下面的 json 格式输出评价名称和评价描述\n" +
            "```\n" +
            "{\"resultName\": \"评价名称\", \"resultDesc\": \"评价描述\"}\n" +
            "```\n" +
            "3. 返回格式必须为 JSON 对象";

    private String getAiTestScoringUserMessage(App app, List<QuestionContentDTO> questionContentDTOList, List<String> choices) {
        StringBuilder userMessage = new StringBuilder();
        userMessage.append(app.getAppName()).append("\n");
        userMessage.append(app.getAppDesc()).append("\n");
        List<QuestionAnswerDTO> questionAnswerDTOList = new ArrayList<>();
        //遍历用户答案，可能存在未答完的情况
        for (int i = 0; i < choices.size(); i++) {
            QuestionAnswerDTO questionAnswerDTO = new QuestionAnswerDTO();
            questionAnswerDTO.setTitle(questionContentDTOList.get(i).getTitle());
            questionAnswerDTO.setUserAnswer(choices.get(i));
            questionAnswerDTOList.add(questionAnswerDTO);
        }
        userMessage.append(JSONUtil.toJsonStr(questionAnswerDTOList));
        return userMessage.toString();
    }



    @Override
    public UserAnswer doScore(List<String> choices, App app) throws Exception {
        //首先看是否击中缓存
        String cacheIfPresent = cache.getIfPresent(getCacheKey(app.getId(), choices));
        if (cacheIfPresent != null) {
            //缓存击中
            ScoringResult result = JSONUtil.toBean(cacheIfPresent, ScoringResult.class);
            return getUserAnswer(choices, app, result);
        }
        //若缓存未命中,争锁
        RLock lock = redissonClient.getLock(AI_ANSWER_LOCK + getCacheKey(app.getId(), choices));
        if (lock.tryLock(3,5, TimeUnit.SECONDS)){
            //争锁成功
            try {
                //得到app的题目列表
                Long appId = app.getId();
                LambdaQueryWrapper<Question> questionLambdaQueryWrapper = new LambdaQueryWrapper<>();
                questionLambdaQueryWrapper.eq(Question::getAppId, appId);
                Question question = questionService.getOne(questionLambdaQueryWrapper);
                //根据题目的答案和用户答案进行打分
                QuestionVO questionVO = QuestionVO.objToVo(question);
                List<QuestionContentDTO> questionContent = questionVO.getQuestionContent();
                //拼接用户prompt
                String userPrompt = getAiTestScoringUserMessage(app, questionContent, choices);
                //调用大模型接口
                String resultJson = aiManager.solveWithChatGLM4(AI_TEST_SCORING_SYSTEM_MESSAGE, userPrompt);
                //解析返回结果,构造返回体
                int start = resultJson.indexOf("{");
                int end = resultJson.lastIndexOf("}");
                ScoringResult result = JSONUtil.toBean(resultJson.substring(start,end + 1), ScoringResult.class);
                //缓存结果
                cache.put(getCacheKey(appId,choices),JSONUtil.toJsonStr(result));
                return getUserAnswer(choices, app, result);
            } catch (Exception e) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, e.getMessage());
            } finally {
                //判断是不是自己的锁
                if (lock != null && lock.isHeldByCurrentThread() && lock.isLocked()) {
                    lock.unlock();
                }
            }
        }else {
            //争锁失败直接返回空
            return null;
        }



    }

    @NotNull
    private UserAnswer getUserAnswer(List<String> choices, App app, ScoringResult result) {
        UserAnswer userAnswer = new UserAnswer();
        userAnswer.setScoringStrategy(ScoringStrategyEnum.AI.getValue());
        userAnswer.setAppId(app.getId());
        userAnswer.setAppType(app.getAppType());
        userAnswer.setChoices(JSONUtil.toJsonStr(choices));
        userAnswer.setResultName(result.getResultName());
        userAnswer.setResultDesc(result.getResultDesc());
        return userAnswer;
    }


    /**
     * 获取本地缓存key
     * @param appId
     * @param choices
     * @return
     */
    private String getCacheKey(Long appId, List<String> choices) {
        return DigestUtil.md5Hex(appId + JSONUtil.toJsonStr(choices));
    }
}
